<script setup lang="ts">
import { getUploadConfig } from '@/api/upload';
import axios from 'axios';
import { useMessage, useDialog } from 'naive-ui';
import { computed, ref } from 'vue';
import { EyeOutlined, DeleteOutlined, PlusOutlined } from '@vicons/antd';
import LoadingPlugin from '@/plugins/loading';
import { splicingCover } from '@/utils';

const props = defineProps({
  // 上传图片格式
  accept: {
    type: String,
    required: false,
    default: '.jpg,.png,.jpeg',
  },
  // 限制文件大小
  size: {
    type: Number,
    default: 0,
  },
  // 限制文件数量
  max: {
    type: Number,
    default: undefined,
  },
  multiple: {
    type: Boolean,
    default: false,
  },
  // 获取配置必须 video image audio
  uploadType: {
    type: String,
    default: 'image',
  },
  width: {
    type: Number,
    default: 96,
  },
  height: {
    type: Number,
    default: 96,
  },
  verifyWH: {
    type: Boolean,
    default: false,
  },
  verifyW: {
    type: Number,
    default: 0,
  },
  verifyH: {
    type: Number,
    default: 0,
  },
});

interface FileList {
  url: string;
  id?: number;
  file_name?: string;
  name?: string;
  file_path?: string;
  file_size?: number;
  file_type?: string;
  expired_at?: string;
  ext?: any;
}

const fileList = defineModel('value', {
  type: Array<FileList>,
  default: () => [],
});

const getCSSProperties = computed(() => {
  return {
    width: `${props.width}px`,
    height: `${props.height}px`,
  };
});

const showModal = ref(false);
const previewUrl = ref('');

const message = useMessage();
const dialog = useDialog();
const uploadRef = ref(null);
const emits = defineEmits(['on-change']);

// 自定义上传
const customRequest = (params: any) => {
  // console.log(params);
  LoadingPlugin.open();
  getUploadConfig({
    scene: props.uploadType,
    file_name: params.file.name,
  })
    .then((res) => {
      // console.log(res);
      const { policy, accessid, signature, host, callback, dir } = res;
      const formData = new FormData();
      // 组装自定义参数
      if (Object.keys(res['callback-var']).length) {
        for (const [key, value] of Object.entries(res['callback-var'])) {
          formData.append(key, value);
        }
      }
      formData.append('policy', policy);
      formData.append('OSSAccessKeyId', accessid);
      formData.append('signature', signature);
      formData.append('host', host);
      formData.append('callback', callback);
      formData.append('key', dir);
      formData.append('file', params.file.file);
      axios
        .post(host, formData, {
          onUploadProgress: (progressEvent) => {
            // 下载进度处理逻辑
            params.onProgress({
              percent: Math.round((progressEvent.loaded * 100) / progressEvent.total),
            });
          },
        })
        .then((postData) => {
          if (!postData.data.verify) {
            params.onError();
            return message.error(postData.data.error_message);
          }
          params.onFinish();

          let fileData = postData.data?.data;
          // onFinish({ file: params.file, data: { ...fileData, filename: params.file.name } });
          fileList.value.push(fileData);
          emits('on-change', fileList.value);
          LoadingPlugin.close();
        })
        .catch((error) => {
          LoadingPlugin.close();
          params.onError();
        });
    })
    .catch(() => {
      LoadingPlugin.close();
      params.onError();
    });
};

// 上传之前校验
const beforeUpload = async ({ file }) => {
  // 类型限制
  if (props.accept) {
    let list = file.name.split('.');
    const fileType = '.' + list[list.length - 1];
    const typeList = props.accept.replace(/\s*/g, '').split(',');
    if (!typeList.includes(fileType.toLowerCase())) {
      message.error(`文件格式不符合要求`);
      return false;
    }
  }
  // console.log(file.file.size, props.size);
  // 大小限制
  if (props.size && file.file.size > 1048576 * props.size) {
    message.error(`文件不能大于${props.size}M`);
    return false;
  }
  //判断是不是符合宽高
  if (props.verifyWH) {
    return await limitFileWH(props.verifyW, props.verifyH, file.file);
  }
};

//限制图片尺寸
const limitFileWH = (E_width: Number, E_height: Number, file: any) => {
  let imgWidth = 0;
  let imgHeight = 0;
  return new Promise(function (resolve, reject) {
    let width = E_width;
    let height = E_height;
    let _URL = window.URL || window.webkitURL;
    let img = new Image();
    img.onload = function () {
      console.log(img.width, img.height);
      imgWidth = img.width;
      imgHeight = img.height;
      let valid = img.width == width && img.height == height;
      valid ? resolve() : reject();
    };
    img.src = _URL.createObjectURL(file);
  }).then(
    () => {
      if (imgHeight !== E_height && imgWidth !== E_width) {
        message.error('分辨率限制为' + E_width + '*' + E_height);
        return false;
      } else {
        return true;
      }
    },
    () => {
      message.error('分辨率限制为' + E_width + '*' + E_height);
      return false;
    }
  );
};

//预览
const preview = ({ url }) => {
  showModal.value = true;
  previewUrl.value = changeUrl(url);
};

//删除
const remove = (index: number) => {
  dialog.info({
    title: '提示',
    content: '你确定要删除吗？',
    positiveText: '确定',
    negativeText: '取消',
    onPositiveClick: () => {
      fileList.value.splice(index, 1);
      emits('on-change', fileList.value);
    },
    onNegativeClick: () => {},
  });
};

/**
 * 判断显示那种类型
 * @param url
 */
const changeUrl = (url: string) => {
  if (props.uploadType === 'video') {
    return url + splicingCover();
  } else if (props.uploadType === 'audio') {
    return url;
  } else {
    return url;
  }
};

defineExpose({
  uploadRef,
});
</script>

<template>
  <div class="w-full">
    <div class="upload">
      <div class="upload-card">
        <!--图片列表-->
        <div
          class="upload-card-item"
          :style="getCSSProperties"
          v-for="(item, index) in fileList"
          :key="`file_${index}`"
        >
          <div class="upload-card-item-info">
            <div class="img-box">
              <img :src="changeUrl(item.url)" class="h-full m-auto" />
            </div>
            <div class="img-box-actions">
              <n-icon size="18" class="mx-2 action-icon" v-if="item.url" @click="preview(item)">
                <EyeOutlined />
              </n-icon>
              <n-icon size="18" class="mx-2 action-icon" @click="remove(index)">
                <DeleteOutlined />
              </n-icon>
            </div>
          </div>
        </div>

        <!--上传图片-->
        <div
          class="upload-card-item upload-card-item-select-picture"
          v-if="fileList.length === 0 && !multiple"
          :style="getCSSProperties"
        >
          <n-upload
            class="w-auto"
            :accept="accept"
            :show-file-list="false"
            ref="uploadRef"
            :on-before-upload="beforeUpload"
            :custom-request="customRequest"
            :multiple="multiple"
            :max="max"
          >
            <slot>
              <div class="flex flex-col justify-center">
                <n-icon size="18" class="m-auto">
                  <PlusOutlined />
                </n-icon>
                <span class="upload-title">点击上传</span>
              </div>
            </slot>
          </n-upload>
        </div>
      </div>
    </div>
  </div>

  <!--预览图片-->
  <n-modal
    v-model:show="showModal"
    preset="card"
    title="预览"
    :bordered="false"
    :style="{ width: '520px' }"
  >
    <img :src="previewUrl" class="m-auto" />
  </n-modal>
</template>

<style scoped lang="less">
.upload {
  width: 100%;
  overflow: hidden;

  &-card {
    width: auto;
    height: auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;

    &-item {
      margin: 0 8px 8px 0;
      position: relative;
      padding: 8px;
      border: 1px solid #d9d9d9;
      border-radius: 2px;
      display: flex;
      justify-content: center;
      flex-direction: column;
      align-items: center;

      &:hover {
        background: 0 0;

        .upload-card-item-info::before {
          opacity: 1;
        }

        &-info::before {
          opacity: 1;
        }
      }

      &-info {
        position: relative;
        height: 100%;
        width: 100%;
        padding: 0;
        overflow: hidden;

        &:hover {
          .img-box-actions {
            opacity: 1;
          }
        }

        &::before {
          position: absolute;
          z-index: 1;
          width: 100%;
          height: 100%;
          background-color: rgba(0, 0, 0, 0.5);
          opacity: 0;
          transition: all 0.3s;
          content: ' ';
        }

        .img-box {
          position: relative;
          height: 100%;
        }

        .img-box-actions {
          position: absolute;
          top: 50%;
          left: 50%;
          z-index: 10;
          white-space: nowrap;
          transform: translate(-50%, -50%);
          opacity: 0;
          transition: all 0.3s;
          display: flex;
          align-items: center;
          justify-content: space-between;

          &:hover {
            background: 0 0;
          }

          .action-icon {
            color: rgba(255, 255, 255, 0.85);

            &:hover {
              cursor: pointer;
              color: #fff;
            }
          }
        }
      }
    }

    &-item-select-picture {
      border: 1px dashed #d9d9d9;
      border-radius: 2px;
      cursor: pointer;
      background: #fafafa;
      color: #666;

      .upload-title {
        color: #666;
      }
    }
  }
}
</style>
